package at.ac.tuwien.dse.health.entity;

import javax.persistence.*;
import java.io.Serializable;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Iterables.*;
import static com.google.common.collect.Lists.newArrayList;

/**
 * Credentials for authenticating in HEALTH.
 *
 * @author Bernhard Nickel
 * @author Gregor Schauer
 * @author Dominik Strasser
 */
@Entity
@NamedQueries({
		@NamedQuery(name = Account.BY_USERNAME_PASSWORD, query = "SELECT a FROM Account a WHERE username = :username AND password = :password"),
		@NamedQuery(name = Account.BY_PATIENT_ID, query = "SELECT a FROM Account a WHERE patient.id = :id"),
		@NamedQuery(name = Account.BY_DOCTOR_ID, query = "SELECT a FROM Account a WHERE doctor.id = :id"),
		@NamedQuery(name = Account.BY_HOSPITAL_ID, query = "SELECT a FROM Account a WHERE hospital.id = :id")
})
public class Account implements Serializable {
	public static final String BY_USERNAME_PASSWORD = "Account#byUsernamePassword";
	public static final String BY_PATIENT_ID = "Account#byPatientId";
	public static final String BY_DOCTOR_ID = "Account#byDoctorId";
	public static final String BY_HOSPITAL_ID = "Account#byHospitalId";
	private Long id;
	private String username;
	private String password;
	private String email;
	private Patient patient;
	private Doctor doctor;
	private Hospital hospital;

	/**
	 * Validates the internal state and returns the associated user.
	 * <p/>
	 * If the state is not valid i.e., this {@link Account} is shared among multiple users, an
	 * {@link IllegalStateException} will be thrown.
	 *
	 * @return the user
	 * @throws IllegalStateException if the internal state is not valid
	 */
	@Transient
	public Serializable getUser() {
		Iterable<Serializable> users = filter(newArrayList(getPatient(), getDoctor(), getHospital()), notNull());
		checkState(size(users) == 1, "Invalid account.");
		return getFirst(users, null);
	}

	/**
	 * Returns the role of the associated user.
	 *
	 * @return the user's role
	 */
	@Transient
	public Class<? extends Serializable> getRole() {
		Serializable user = getUser();
		return user != null ? user.getClass() : null;
	}

	@Override
	public String toString() {
		return username;
	}

	@Id
	@GeneratedValue
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	@Basic(optional = false)
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Basic(optional = false)
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Basic(optional = false)
	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	@OneToOne
	public Patient getPatient() {
		return patient;
	}

	public void setPatient(Patient patient) {
		this.patient = patient;
	}

	@OneToOne
	public Doctor getDoctor() {
		return doctor;
	}

	public void setDoctor(Doctor doctor) {
		this.doctor = doctor;
	}

	@OneToOne
	public Hospital getHospital() {
		return hospital;
	}

	public void setHospital(Hospital hospital) {
		this.hospital = hospital;
	}
}
